using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using AspectCore.Extensions.Reflection;
using netCoreStudy.Attr.QueryAttr;
using netCoreStudy.Entities.Base;
using netCoreStudy.Enum;

namespace netCoreStudy.Express
{
	/// <summary>
	///  whereExp 的 动态 linq 写法
	/// </summary>
	public static class WhereExt3
	{
		public static IQueryable<TSource> WhereExp3<TSource, TP>(this IQueryable<TSource> queryList, TP parameter)
			where TP : BaseParameter
		{
			Type typeSource = typeof(TSource); // 获取 tSource 类型
			Type typeParameter = parameter.GetType(); // 获取 tParameter 类型
			PropertyReflector[] typeSourceProps = typeSource.GetProps(); // 获取 tSource 下的所有 属性 
			PropertyReflector[] typeParameterProps = typeParameter.GetProps() // 获取 tParameter 下的所有 属性(带where特性的) 
				.Where(x => x.GetCustomAttribute<WhereAttribute>() != null)
				.ToArray();

			// 遍历 tParam 的  属性 
			foreach (PropertyReflector tPProp in typeParameterProps)
			{
				Type type = tPProp.GetPropertyInfo().PropertyType; // 获取 tPProp的类型
				object value = tPProp.GetValue(parameter); // 获取 tPProp 的值
				if (type.PropIsZeroOrNull(value)) continue; // 如果值是0 就跳过 
				WhereAttribute whereAttr = tPProp.GetCustomAttribute<WhereAttribute>(); // 获取where 特性
				string displayName = whereAttr.DisplayName;
				string blurry = whereAttr.Blurry;
				if (displayName != null)
				{
					//  TSource 的 属性 列表里面 是否  有 displayName 
					if (typeSourceProps.All(x => x.Name != displayName))
						throw new Exception($"{typeSource.Name} 里面 不含{displayName} 这个属性!");
					string predicate = $"it.{displayName} {whereAttr.Query.QueryToStr()} {value}";
					Console.WriteLine(predicate);
					queryList = queryList.Where(predicate, value);
				}

				if (blurry != null)
				{
					List<string> propNames = typeSourceProps.Select(x => x.Name).ToList();
					string[] strings = blurry.Split(',').Distinct().ToArray();
					// 检查 类里面 的属性 (大数组)  有没有 完全包含  blurry[](小数组)(大数组是否包含小数组用这个)
					if (strings.All(x => propNames.Contains(x.Split('.')[0])) == false)
						throw new Exception(
							$"{typeParameter.Name},特性里Blurry的{tPProp.Name}这个属性名有问题! {typeSource.Name}类里面 没有 {blurry} 这个属性! ");
					string expStr = "";
					// 拼一下 拼成 x=>x.name.contains("lala") || x.user.Name.contains("lala") 这样的
					for (var i = 0; i < strings.Length; i++)
					{
						string blurryName = strings[i];
						expStr += i == strings.Length - 1
							? $"it.{blurryName}.ToLower().Contains(\"{value.ToString()?.ToLower().Trim()}\")  "
							: $"it.{blurryName}.ToLower().Contains(\"{value.ToString()?.ToLower().Trim()}\") || ";
					}

					Console.WriteLine(expStr);
					queryList = queryList.Where(expStr);
				}
			}

			return queryList;
		}

		/// <summary>
		/// 将 queryType 转换 为 字符串
		/// </summary>
		/// <param name="queryType"></param>
		/// <returns></returns>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		public static string QueryToStr(this QueryType queryType)
		{
			//根据查询选择器中该属性的查询条件进行不同的操作	
			//根据 枚举 获取 运算 符
			switch (queryType)
			{
				case QueryType.Equal:
					return "==";
				case QueryType.GreaterThan:
					return ">";
				case QueryType.LessThan:
					return "<";
				case QueryType.GreaterThanOrEqual:
					return ">=";
				case QueryType.LessThanOrEqual:
					return "<=";
				default:
					throw new ArgumentOutOfRangeException();
			}
		}

		/// <summary>
		/// 检查 属性 的值 是不是为0 或者 是 空的
		/// </summary>
		/// <param name="type"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public static bool PropIsZeroOrNull(this Type type, object value)
		{
			// 如果 类型 是 int 并且 值为0 ，那么 就是查询全部 ，就 跳过 where 
			if (type == typeof(int) && (int) value == 0) return true;
			// 如果 类型 是 int  并且 值为0 ，那么 就是查询全部 ，就 跳过 where 
			if (type == typeof(long) && (long) value == 0) return true;
			// 如果 类型 是 枚举 并且 值为0 ，那么 就是查询全部 ，就 跳过 where 
			if (type.IsEnum && (int) value == 0) return true;

			// 如果 类型 是 string 并且 值为 null 或者 "" ，那么 就是查询全部 ，就 跳过 where 
			if (type == typeof(string))
			{
				if (value == null) return true;
				if (string.IsNullOrWhiteSpace(value.ToString())) return true;
			}

			return false;
		}
	}
}